package setting_the_scene;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GraphicsConfiguration;

import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.PointLight;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.JFrame;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.universe.SimpleUniverse;

/**
 * This class generates a scene graph and displays it on a 3d canvas using
 * Java3D libraries.
 * 
 * @author Richard
 * 
 */
public class RayTracingSceneTest extends JFrame {

	private static final long serialVersionUID = -6911175101166329013L;

	/**
	 * Sets the JFrame parameters and sets up the branch group for the scene.
	 * Sets up a default viewing platform and adds the canvas 3d to the frame.
	 */
	public RayTracingSceneTest() {
		super("Ray Tracing - Assignment 1 - Setting The Scene");

		this.setLayout(new BorderLayout());
		this.setSize(800, 600);
		this.setLocation(300, 300);
		this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

		GraphicsConfiguration config = SimpleUniverse
				.getPreferredConfiguration();
		SceneTestCanvas canvas3D = new SceneTestCanvas(config);
		this.add(canvas3D);

		BranchGroup scene = createSceneGraph();

		SimpleUniverse simpleU = new SimpleUniverse(canvas3D);
		simpleU.getViewingPlatform().setNominalViewingTransform();

		scene.compile();

		simpleU.addBranchGraph(scene);
	}

	/**
	 * Creates two spheres, a floor quad, a point light, and an ambient light
	 * and adds them to the branch group.
	 * 
	 * @return The branch group containing all scene objects.
	 */
	private BranchGroup createSceneGraph() {
		BranchGroup scene = new BranchGroup();

		Sphere s1 = new Sphere(0.33f);
		TransformGroup sphereOneGroup = new TransformGroup();
		Transform3D sphereOneTransform = new Transform3D();
		sphereOneTransform.setTranslation(new Vector3f(0.05f, 0.0f, 0.0f));
		sphereOneGroup.setTransform(sphereOneTransform);
		sphereOneGroup.addChild(s1);

		Sphere s2 = new Sphere(0.3f);
		TransformGroup sphereTwoGroup = new TransformGroup();
		Transform3D sphereTwoTransform = new Transform3D();
		sphereTwoTransform.setTranslation(new Vector3f(-0.4f, -0.15f, -0.5f));
		sphereTwoGroup.setTransform(sphereTwoTransform);
		sphereTwoGroup.addChild(s2);

		scene.addChild(sphereOneGroup);
		scene.addChild(sphereTwoGroup);

		PointLight light = new PointLight(
				new Color3f(new Color(255, 255, 255)), new Point3f(0.3f, 1.0f,
						1.0f), new Point3f(1.0f, 0.0f, 0.0f));
		light.setInfluencingBounds(new BoundingSphere(new Point3d(0, 0, 0),
				100.0f));

		AmbientLight aLight = new AmbientLight(new Color3f(new Color(255, 255,
				255)));
		aLight.setInfluencingBounds(new BoundingSphere(new Point3d(0, 0, 0),
				100.0f));

		scene.addChild(aLight);
		scene.addChild(light);

		scene.addChild(new Shape3D(this.createFloorQuad(), new Appearance()));

		return scene;
	}

	/**
	 * Sets the vertices of the floor quad and adds them to a quad array.
	 * 
	 * @return The floor quad array.
	 */
	private QuadArray createFloorQuad() {
		QuadArray floorQuad = new QuadArray(4, QuadArray.COORDINATES
				+ QuadArray.COLOR_3 + QuadArray.NORMALS);

		// v0
		Point3f v0 = new Point3f(1.0f, -0.7f, 1.0f);
		floorQuad.setCoordinate(3, v0);
		// v1
		Point3f v1 = new Point3f(-3.0f, -0.7f, 1.0f);
		floorQuad.setCoordinate(2, v1);
		// v2
		Point3f v2 = new Point3f(-3.0f, -0.7f, -10.0f);
		floorQuad.setCoordinate(1, v2);
		// v3
		Point3f v3 = new Point3f(1.0f, -0.7f, -10.0f);
		floorQuad.setCoordinate(0, v3);

		// v0
		floorQuad.setColor(0, new Color3f(new Color(100, 100, 100)));
		// v1
		floorQuad.setColor(1, new Color3f(new Color(100, 100, 100)));
		// v2
		floorQuad.setColor(2, new Color3f(new Color(100, 100, 100)));
		// v3
		floorQuad.setColor(3, new Color3f(new Color(100, 100, 100)));

		// v0
		floorQuad.setNormal(0, new Vector3f(0.0f, 0.0f, 1.0f));
		// v1
		floorQuad.setNormal(1, new Vector3f(0.0f, 0.0f, 1.0f));
		// v2
		floorQuad.setNormal(2, new Vector3f(0.0f, 0.0f, 1.0f));
		// v3
		floorQuad.setNormal(3, new Vector3f(0.0f, 0.0f, 1.0f));

		return floorQuad;
	}

	@Override
	public void dispose() {
		System.exit(0);
	}

	/**
	 * Creates a new JFrame and makes it visible.
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		RayTracingSceneTest world = new RayTracingSceneTest();
		world.setVisible(true);
	}

}
